/*
 * Copyright (c) 2010, Jim Hollinger
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Jim Hollinger nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef COMMON_DATACHANNEL_RAWSOCKETCHANNEL_H_
#define COMMON_DATACHANNEL_RAWSOCKETCHANNEL_H_

#include <stdio.h>
#include <string>

#if defined (WIN32)
#  include "winstdint.h"
#else
#  include <stdint.h>
#endif

#include "datachannel/socketchannel.h"

namespace datachannel {

/**
 ****************************************************************************
 * @brief  A class to exchanging data over a raw network socket
 *          using the pcap library.
 ****************************************************************************
**/
class RawSocketChannel : public SocketChannel {
  public:

#pragma pack(push)
#pragma pack(1)

    // 4 byte IPv4 address
    struct IPv4_address {
        uint8_t byte1;
        uint8_t byte2;
        uint8_t byte3;
        uint8_t byte4;
    };

    // 6 byte MAC
    struct MAC_address {
        uint8_t byte1;
        uint8_t byte2;
        uint8_t byte3;
        uint8_t byte4;
        uint8_t byte5;
        uint8_t byte6;
    };

    // MAC header
    struct MAC_header {
        MAC_address dmac;           // Destination MAC
        MAC_address smac;           // Source MAC
        uint16_t    ether_type;     // Ether type
    };

    // IPv4 header
    struct IP_header {
        uint8_t     ver_ihl;        // Version (4 bits) + Internet header length (4 bits)
        uint8_t     tos;            // Type of service
        uint16_t    tlen;           // Total length
        uint16_t    identification; // Identification
        uint16_t    flags_fo;       // Flags (3 bits) + Fragment offset (13 bits)
        uint8_t     ttl;            // Time to live
        uint8_t     proto;          // Protocol
        uint16_t    crc;            // Header checksum
        uint32_t    saddr;          // Source address
        uint32_t    daddr;          // Destination address
        uint32_t    op_pad;         // Option + Padding
    };

    // ARP header
    struct ARP_header {
        uint16_t    htype;          // Hardware type
        uint16_t    ptype;          // Protocol type
        uint8_t     hlen;           // Hardware address length
        uint8_t     plen;           // Protocol address length
        uint16_t    oper;           // Operation
        MAC_address sha;            // Sender hardware address
        uint32_t    spa;            // Sender protocol address
        MAC_address tha;            // Target hardware address
        uint32_t    tpa;            // Target protocol address
    };

    // ICMP header
    struct ICMP_header {
        uint8_t     type;           // Type
        uint8_t     code;           // Code
        uint16_t    crc;            // Checksum
        uint16_t    id;             // ID
        uint16_t    seq;            // Sequence
    };

    // IGMP header
    struct IGMP_header {
        uint8_t     type;           // Type
        uint8_t     mrt;            // Max response time
        uint16_t    crc;            // Checksum
        uint32_t    gaddr;          // Group address
    };

    // TCP header
    struct TCP_header {
        uint16_t    sport;          // Source port
        uint16_t    dport;          // Destination port
        uint32_t    seq_num;        // Sequence number
        uint32_t    ack_num;        // Acknowledgment number
        uint8_t     offset;         // Data offset
        uint8_t     flags;          // Flags
        uint16_t    window;         // Window size
        uint16_t    crc;            // Checksum
        uint16_t    urgent;         // Urgent pointer
    };

    // UDP header
    struct UDP_header {
        uint16_t    sport;          // Source port
        uint16_t    dport;          // Destination port
        uint16_t    len;            // Datagram length
        uint16_t    crc;            // Checksum
    };

#pragma pack(pop)

    // IPv4 protocols
    enum IPv4_proto {
        HOPOPT      =  0,
        ICMP        =  1,
        IGMP        =  2,
        GGP         =  3,
        IP          =  4,
        ST          =  5,
        TCP         =  6,
        CBT         =  7,
        EGP         =  8,
        IGP         =  9,
        BBN_RCC_MON = 10,
        NVP_II      = 11,
        PUP         = 12,
        ARGUS       = 13,
        EMCON       = 14,
        XNET        = 15,
        CHAOS       = 16,
        UDP         = 17,
        MUX         = 18,
        DCN_MEAS    = 19,
        HMP         = 20,
        PRM         = 21,
        XNS_IDP     = 22
    };

    // IGMP protocols
    enum IGMP_type {
        IGMP_QUERY      = 0x11,
        IGMPv1_REPORT   = 0x12,
        IGMPv2_REPORT   = 0x16,
        IGMP_LEAVE      = 0x17,
        IGMPv3_REPORT   = 0x22,
    };

  public:
    explicit RawSocketChannel(logger::Logger *logger);
    virtual ~RawSocketChannel();
    virtual bool open(const std::string &descriptor);
    virtual bool close();
    virtual int read(void *buffer, int maxCount);
    virtual int write(const void *buffer, int count);

    static const char *ip4ToString(uint32_t in);
    static const char *ip6ToString(struct sockaddr *sockaddr, char *address, int addrlen);
    static void if_print(const void *pcap_interface);

  protected:
    void *openInterface(const char *interface_name,
                        const char *interface_description,
                        const char *packet_filter,
                        sockaddr   *interface_netmask);
    int processPacket(const void *raw_data, int raw_len, void *buffer, int maxCount);

  protected:
    static bool printLibVersion_flag;
    void   *pcap;
    void   *bpf;
    char    description[256];
};

}  // namespace datachannel

#endif  // COMMON_DATACHANNEL_RAWSOCKETCHANNEL_H_
